package com.linkgie.galaxyframework.utils.service;

import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.ProviderException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;

import com.linkgie.galaxyframework.utils.CollectionUtils;
import com.linkgie.galaxyframework.utils.annotations.Name;

/**
 * ServiceContext 命名服务上下文；
 * 
 * 管理了某一特定类型的一组命名服务实例；
 * 
 * @author Hike
 *
 * @param <T> 命名服务的类型
 */
public class ServiceContext<T> {

	private Class<T> serviceClazz;

	/**
	 * 由上下文提供的服务的所有实现；
	 * <p>
	 * fullName - Provider 映射表；
	 */
	private Map<String, ServiceProvider<T>> providers = new LinkedHashMap<>();

	private AccessControlContext acc;

	public static <T> ServiceContext<T> load(Class<T> serviceClazz) {
		ClassLoader classLoader = getDefaultClassLoader();
		return load(serviceClazz, classLoader);
	}

	public static <T> ServiceContext<T> load(Class<T> serviceClazz, ClassLoader classLoader) {
		ServiceContext<T> providerContext = new ServiceContext<>(serviceClazz);
		providerContext.loadFrom(classLoader);
		return providerContext;
	}

	private ServiceContext(Class<T> serviceClazz) {
		this.serviceClazz = serviceClazz;
		this.acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
	}

	private static ClassLoader getDefaultClassLoader() {
		// 默认采用线程上下文的类加载器；避免直接采用系统的类加载器: ClassLoader.getSystemClassLoader() ;
		return Thread.currentThread().getContextClassLoader();
	}

	public void loadFrom(ClassLoader classLoader) {
		ServiceLoader<T> sl = ServiceLoader.load(serviceClazz, classLoader);
		loadAll(sl);
	}

	private synchronized void loadAll(ServiceLoader<T> serviceLoader) {
		for (T service : serviceLoader) {
			install(service);
		}
	}

	/**
	 * 安装指定的服务提供者；<br>
	 * 
	 * 如果 {@link ServiceProvider#getFullName()} 同名的服务提供者已经存在，则返回 false；<br>
	 * 
	 * 如果同名的服务提供者不存在，则返回 false；
	 * 
	 * @param service 提供者的服务实现；
	 * @return
	 */
	private synchronized boolean install(T service) {
		ServiceProvider<T> provider = ServiceProvider.sigleton(service);
		if (providers.containsKey(provider.getFullName())) {
			return false;
		}
		providers.put(provider.getFullName(), provider);
		return true;
	}

	public T install(String providerFullName) {
		return install(providerFullName, null);
	}

	public T install(String providerFullName, ClassLoader classLoader) {
		// 默认采用线程上下文的类加载器；避免直接采用系统的类加载器: ClassLoader.getSystemClassLoader() ;
		final ClassLoader cl = (classLoader == null) ? getDefaultClassLoader() : classLoader;
		T service = null;
		if (acc == null) {
			service = createInstance(providerFullName, cl);
		} else {
			service = AccessController.doPrivileged(new PrivilegedAction<T>() {
				public T run() {
					return createInstance(providerFullName, cl);
				}
			}, acc);
		}
		if (!install(service)) {
			throw new ProviderException(
					"[" + serviceClazz.getName() + "] Provider " + providerFullName + " already exist!");
		}
		return service;
	}

	public int getSize() {
		return providers.size();
	}

	public Collection<ServiceProvider<T>> getProviders() {
		return providers.values();
	}

	public ServiceProvider<T> getProvider(String providerFullName) {
		return providers.get(providerFullName);
	}

	/**
	 * 返回所有服务实例；
	 * 
	 * @return
	 */
	public Collection<T> getServices() {
		return CollectionUtils.cast(providers.values(), p -> p.getService());
	}
	
	public T getFirstService() {
		if (providers.size() == 0) {
			return null;
		}
		return providers.values().iterator().next().getService();
	}

	/**
	 * 返回所有服务实例；
	 * 
	 * @return
	 */
	public T[] getServicesArray() {
		return CollectionUtils.castToArray(providers.values(), serviceClazz, p -> p.getService());
	}

	/**
	 * 返回指定名称的命名服务实例；
	 * <p>
	 * “命名服务”是指实现类通过标注 {@link Name} 指定了名称的服务；
	 * <p>
	 * 
	 * 如果有多个重名服务，此方法只会返回搜索过程中的第一个匹配项，由于排序的不确定性，所以返回的具体实例是不确定的；
	 * 
	 * @param annotatedName
	 * @return
	 */
	public T getService(String annotatedName) {
		for (ServiceProvider<T> provider : providers.values()) {
			if (annotatedName.equals(provider.getAnnotatedName())) {
				return provider.getService();
			}
		}
		return null;
	}

	/**
	 * 返回指定名称的全部命名服务实例；
	 * <p>
	 * “命名服务”是指实现类通过标注 {@link Name} 指定了名称的服务；
	 * <p>
	 * 
	 * @param annotatedName
	 * @return
	 */
	public T[] getServices(String annotatedName) {
		List<T> services = new ArrayList<>();
		for (ServiceProvider<T> provider : providers.values()) {
			if (annotatedName.equals(provider.getAnnotatedName())) {
				services.add(provider.getService());
			}
		}
		return CollectionUtils.toArray(services, serviceClazz);
	}

	private T createInstance(String className, ClassLoader classLoader) {
		Class<?> implClazz = loadClass(className, classLoader);
		try {
			T provider = serviceClazz.cast(implClazz.newInstance());
			return provider;
		} catch (Throwable e) {
			throw new ProviderException("[" + serviceClazz.getName() + "] Provider " + className
					+ " could not be instantiated! --" + e.getMessage());
		}
	}

	private Class<?> loadClass(String className, ClassLoader classLoader) {
		Class<?> implClazz = null;
		try {
			implClazz = Class.forName(className, false, classLoader);
		} catch (ClassNotFoundException x) {
			throw new ProviderException("[" + serviceClazz.getName() + "] Provider " + className + " not found");
		}
		if (!serviceClazz.isAssignableFrom(implClazz)) {
			throw new ProviderException("[" + serviceClazz.getName() + "] Provider " + className + " not a subtype");
		}
		return implClazz;
	}
}